package org.xingole.jgit.lib;

/**
 * A Ref that points directly at an ObjectId.
 *
 * <p>This class is abstract so the one template the most common methods for ObjectIdRef. The more specific details
 * should give over the subclass to implement the details. em... jgit use the internal the subclass pattern to
 * decline the java file numbers. In the meanwhile, the subclasses have to share many common resources but the scala
 * is not too large, then we can adopt jgit this design pattern.</p>
 */
public abstract class ObjectIdRef implements Ref {
    // The required properties for reference directly pointing to the Git object.
    private final String name;

    private final Storage storage;

    private final ObjectId objectId;

    // Constructors
    protected ObjectIdRef(Storage st, String name, ObjectId id) {
        this.name = name;
        this.storage = st;
        this.objectId = id;
    }

    // The implementation for the methods of Ref interface
    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isSymbolic() {
        return false;
    }

    @Override
    public ObjectId getObjectId() {
        return objectId;
    }

    @Override
    public Ref getLeaf() {
        return this;
    }

    @Override
    public Ref getTarget() {
        return this;
    }

    @Override
    public Storage getStorage() {
        return storage;
    }

    @Override
    public String toString() {
        return "Ref[" +
                getName() +
                '=' +
                ObjectId.toString(getObjectId()) +
                ']';
    }

    // The more specific subclasses for ObjectIdRef

    /**
     * Any reference whose peeled value is not yet known
     */
    public static class Unpeeled extends ObjectIdRef {
        /**
         * Create a new ref pairing.
         * @param st - method used to store this ref.
         * @param name - name of this ref.
         * @param id - current value of the ref. May be null to indicate a ref that does not exist yet.
         */
        public Unpeeled(Storage st, String name, ObjectId id) {
            super(st, name, id);
        }

        @Override
        public ObjectId getPeeledObjectId() {
            return null;
        }

        @Override
        public boolean isPeeled() {
            return false;
        }
    }

    /**
     * An annotated tag whose peeled object has been cached.
     */
    public static class PeeledTag extends ObjectIdRef {
        private final ObjectId peeledObjectId;  // cached peeled object id

        /**
         * Create a new ref pairing.
         * @param st - method used to store this ref.
         * @param name - name of this ref.
         * @param id - current value of the ref. May be null to indicate a ref that does not exist yet.
         * @param peeledObjectId - the first non-tag object that tag id points to.
         */
        public PeeledTag(Storage st, String name, ObjectId id, ObjectId peeledObjectId) {
            super(st, name, id);
            this.peeledObjectId = peeledObjectId;
        }

        @Override
        public ObjectId getPeeledObjectId() {
            return peeledObjectId;
        }

        @Override
        public boolean isPeeled() {
            return true;
        }
    }

    /**
     * A reference to a non-tag object coming from a cached source.
     */
    public static class PeeledNonTag extends ObjectIdRef {
        /**
         * Create a new ref pairing.
         * @param st - method used to store this ref.
         * @param name - name of this ref.
         * @param id - current value of the ref. May be null to indicate a ref that does not exist yet.
         */
        public PeeledNonTag(Storage st, String name, ObjectId id) {
            super(st, name, id);
        }

        @Override
        public ObjectId getPeeledObjectId() {
            return null;
        }

        @Override
        public boolean isPeeled() {
            return true;
        }
    }
}
